// Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use paste::paste;

use crate::bincode::Decoder;

pub trait BorrowDecode<'de>: Sized {
    unsafe fn borrow_decode_unchecked<D: Decoder>(decoder: &mut D, buf: &'de [u8]) -> Self;
    fn borrow_decode<D: Decoder>(decoder: &mut D, buf: &'de [u8]) -> Result<Self, D::Error>;    
}

macro_rules! impl_borrow_decode_for_builtin {
    ($type:ident) => {
        impl<'de> BorrowDecode<'de> for $type {
            unsafe fn borrow_decode_unchecked<D: Decoder>(decoder: &mut D, buf: &'de [u8]) -> Self {
                unsafe { paste! { decoder.[<get_ $type _unchecked>](buf) } }
            }
        
            fn borrow_decode<D: Decoder>(decoder: &mut D, buf: &'de [u8]) -> Result<Self, D::Error> {
                paste! { decoder.[<get_ $type>](buf) }
            }
        }
    };
}

impl_borrow_decode_for_builtin!(bool);
impl_borrow_decode_for_builtin!(u8);
impl_borrow_decode_for_builtin!(u16);
impl_borrow_decode_for_builtin!(u32);
impl_borrow_decode_for_builtin!(u64);
impl_borrow_decode_for_builtin!(i8);
impl_borrow_decode_for_builtin!(i16);
impl_borrow_decode_for_builtin!(i32);
impl_borrow_decode_for_builtin!(i64);
impl_borrow_decode_for_builtin!(f32);
impl_borrow_decode_for_builtin!(f64);

impl<'de> BorrowDecode<'de> for &'de [u8] {
    unsafe fn borrow_decode_unchecked<D: Decoder>(decoder: &mut D, buf: &'de [u8]) -> Self {
        let len = unsafe { decoder.get_u32_unchecked(buf) };
        unsafe { decoder.get_bytes_unchecked(buf, len as usize) }
    }

    fn borrow_decode<D: Decoder>(decoder: &mut D, buf: &'de [u8]) -> Result<Self, D::Error> {
        let len = decoder.get_u32(buf)?;
        Ok(decoder.get_bytes(buf, len as usize)?)
    }
}

impl<'de> BorrowDecode<'de> for &'de str {
    unsafe fn borrow_decode_unchecked<D: Decoder>(decoder: &mut D, buf: &'de [u8]) -> Self {
        let len = unsafe { decoder.get_u32_unchecked(buf) };
        unsafe { decoder.get_str_unchecked(buf, len as usize) }
    }

    fn borrow_decode<D: Decoder>(decoder: &mut D, buf: &'de [u8]) -> Result<Self, D::Error> {
        let len = decoder.get_u32(buf)?;
        Ok(decoder.get_str(buf, len as usize)?)
    }
}

impl<'de, T: BorrowDecode<'de>> BorrowDecode<'de> for Option<T> {
    unsafe fn borrow_decode_unchecked<D: Decoder>(decoder: &mut D, buf: &'de [u8]) -> Self {
        let val = unsafe { decoder.get_bool_unchecked(buf) };
        if val {
            Some(unsafe { T::borrow_decode_unchecked(decoder, buf) })
        } else {
            None
        }
    }

    /// # Examples
    /// 
    /// ```
    /// use ohmw_core::bincode::{BorrowDecode, DefaultDecoder};
    /// 
    /// let bytes = vec![1, 2, 0, 0, 0, 1, 2];
    /// let decoded = <Option<&[u8]>>::borrow_decode(&mut DefaultDecoder::new(), &bytes)
    ///     .expect("failed to decode message");
    /// assert_eq!(decoded, Some(vec![1, 2].as_slice()));
    /// ```
    fn borrow_decode<D: Decoder>(decoder: &mut D, buf: &'de [u8]) -> Result<Self, D::Error> {
        let val = decoder.get_bool(buf)?;
        if val {
            Ok(Some(T::borrow_decode(decoder, buf)?))
        } else {
            Ok(None)
        }
    }
}
